page.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. "use client";
  2. import {
  3. getRank,
  4. getRankDetail,
  5. Rank,
  6. RankDetail,
  7. RankItem,
  8. RankRequestParams,
  9. RankReward,
  10. } from "@/api/activity";
  11. import { userInfoApi } from "@/api/login";
  12. import CustomButton from "@/components/CustomButton";
  13. import HeaderBack from "@/components/HeaderBack";
  14. import Tabs from "@/components/Tabs";
  15. import { HeaderImageMap, RankSourceMap } from "@/enums";
  16. import { useRouter } from "@/i18n/routing";
  17. import { cryptoStr } from "@/utils";
  18. import { useRequest } from "ahooks";
  19. import { InfiniteScroll } from "antd-mobile";
  20. import clsx from "clsx";
  21. import Image from "next/image";
  22. import React from "react";
  23. import styles from "./page.module.scss";
  24. const Page = () => {
  25. const { data: userInfo, run: refreshUserInfo } = useRequest<any, any>(userInfoApi, {
  26. pollingErrorRetryCount: 1,
  27. });
  28. const router = useRouter();
  29. const [initLoading, setInitLoading] = React.useState<boolean>(true);
  30. const [areaId, setAreaId] = React.useState<number>(1);
  31. const [loading, setLoading] = React.useState<boolean>(false);
  32. const [data, setData] = React.useState<RankDetail>({} as RankDetail);
  33. const [noMore, setNoMore] = React.useState<boolean>(false);
  34. const [rankList, setRankList] = React.useState<Rank[]>([]);
  35. const [rankData, setRankData] = React.useState<RankItem[]>([]);
  36. const [currentSource, setCurrentSource] = React.useState<any>({
  37. source: 1,
  38. type: 1,
  39. });
  40. // source 来源(1现金投注2现金+彩金投注3免费币投注4重玩币投注5现金充值6邀请玩家)
  41. // type 榜单类型(0小时榜1日榜单2周榜单3月榜)
  42. const pageInfo = React.useRef({
  43. page: 0,
  44. });
  45. const SourceObject = React.useMemo(() => {
  46. if (!rankData?.length) return [];
  47. const source: any = {};
  48. rankData?.forEach((item) => {
  49. if (!source[item.source]) {
  50. source[item.source] = {};
  51. }
  52. source[item.source][item.type] = item;
  53. });
  54. return source;
  55. }, [rankData]);
  56. const Tab1Cfg = React.useMemo(() => {
  57. if (!SourceObject) return [];
  58. const result: any = [];
  59. Object.keys(SourceObject).forEach((key) => {
  60. const curSource = RankSourceMap.get(Number(key)) || ({} as any);
  61. result.push({
  62. id: key,
  63. name: (
  64. <div className="flex items-center justify-center">
  65. <img
  66. className="pic needsclick relative z-[-1] w-[46px]"
  67. src={curSource?.img}
  68. alt=""
  69. />
  70. <div className="needsclick relative w-[80px] whitespace-normal break-all text-center text-[12px] font-black leading-[1]">
  71. {curSource.text}
  72. </div>
  73. </div>
  74. ),
  75. });
  76. });
  77. return result;
  78. }, [SourceObject]);
  79. const Tab2Cfg = React.useMemo(() => {
  80. if (!SourceObject[currentSource.source]) return [];
  81. const result: any = [];
  82. Object.keys(SourceObject[currentSource.source]).forEach((key) => {
  83. const curType = RankSourceMap.get(Number(key)) || ({} as any);
  84. result.push({
  85. id: key,
  86. name: (
  87. <div className="flex flex-col items-center justify-center">
  88. <div className="text-[14px] font-black leading-[1]">DIA</div>
  89. <div className="flex flex-row items-center justify-center gap-[5px] text-[12px] text-[var(--primary-button)]">
  90. <i className="iconfont icon-daojishi-hui !text-[13px]"></i>
  91. <div className="countdownTimer needsclick flex flex-row items-center justify-center gap-[1px]">
  92. <span>01</span>:<span>01</span>:<span>01</span>
  93. </div>
  94. </div>
  95. </div>
  96. ),
  97. });
  98. });
  99. return result;
  100. }, [SourceObject, currentSource]);
  101. const getRankData = async () => {
  102. const res = await getRank();
  103. if (res.code === 200) {
  104. setRankData(res?.data || []);
  105. }
  106. };
  107. React.useEffect(() => {
  108. getRankData();
  109. }, []);
  110. React.useEffect(() => {
  111. pageInfo.current.page = 0;
  112. setRankList([]);
  113. setData({} as any);
  114. // getData(areaId);
  115. }, [areaId]);
  116. const getData = async (id: number) => {
  117. if (loading) return;
  118. try {
  119. setLoading(true);
  120. if (pageInfo.current?.page === 1) {
  121. setInitLoading(true);
  122. }
  123. const params: RankRequestParams = {
  124. area_id: id,
  125. type: 0,
  126. page: pageInfo.current?.page || 1,
  127. };
  128. const res = await getRankDetail(params);
  129. if (res?.code === 200) {
  130. setData(res.data);
  131. if (!res?.data?.list || (res?.data?.list && res?.data?.list?.length < 10)) {
  132. setNoMore(true);
  133. }
  134. setRankList((value) => {
  135. return [...(value || []), ...(res?.data?.list || [])];
  136. });
  137. }
  138. } finally {
  139. setLoading(false);
  140. setInitLoading(false);
  141. }
  142. };
  143. const getNumber = (num: number) => {
  144. if (num > 3) return num;
  145. return <img className="inline-block w-[.18rem]" src={`/rank/f${num}.webp`} alt=""></img>;
  146. };
  147. const sourceChange = (actKey: any, key: any) => {
  148. setCurrentSource((state: any) => ({ ...state, [key]: actKey }));
  149. };
  150. return (
  151. <div className={styles.page}>
  152. <HeaderBack
  153. showBack={true}
  154. title={
  155. <div className="flex items-center justify-center">
  156. <i className="iconfont icon-jiangbei mr-[.1rem] text-[.22rem] text-[var(--textColor4)]"></i>
  157. <span className="leading-[1]">Ranking</span>
  158. </div>
  159. }
  160. />
  161. <main className={"main-header hasFlag stickyFlag"} id="mainBox">
  162. <div className="h-[100%] p-[.1rem]">
  163. <div className={styles.pageContainer}>
  164. <div className={styles.tabTop}>
  165. <Tabs
  166. items={Tab1Cfg}
  167. activeKey={currentSource.source}
  168. onChanage={(val) => sourceChange(val, "source")}
  169. ></Tabs>
  170. </div>
  171. <div className={styles.tabType}>
  172. <Tabs items={Tab2Cfg}></Tabs>
  173. </div>
  174. <div className="min-h-[0] flex-1 overflow-auto">
  175. <div
  176. className={clsx(
  177. styles.total,
  178. "flex w-full flex-row items-stretch gap-[15px] rounded-[var(--borderRadius)] p-[15px]"
  179. )}
  180. >
  181. <img
  182. src="/rank/H7_jackpot_jiangbei_icon.webp"
  183. alt=""
  184. className="w-[100px]"
  185. />
  186. <div className="flex flex-1 flex-col justify-between leading-[1]">
  187. <div className="flex items-center">
  188. <img
  189. className="w-[10px]"
  190. src="/rank/H7_jackpot_yezi_icon.webp"
  191. style={{
  192. transform: "rotateY(180deg)",
  193. transformOrigin: "center",
  194. }}
  195. alt=""
  196. />
  197. <span className="mx-[.1rem] text-[14px] font-black">
  198. Daily Contest
  199. </span>
  200. <img
  201. className="w-[10px]"
  202. src="/rank/H7_jackpot_yezi_icon.webp"
  203. alt=""
  204. />
  205. </div>
  206. <div className="text text-[14px]">Contest prize pool</div>
  207. <div
  208. style={{
  209. boxShadow: "0 0 0 1px var(--primary-button) inset",
  210. }}
  211. className="flex items-center rounded-[var(--borderRadius)] bg-[#fff] py-[6px] pl-[20px] text-[17px] font-black tracking-[2px] text-[var(--textColor4)]"
  212. >
  213. R$ {data?.total || 0}
  214. </div>
  215. <div className="text-[12px]">
  216. 2025-07-30 00:00 ~ 2025-07-31 00:00
  217. </div>
  218. </div>
  219. </div>
  220. <div className="mt-[.1rem] rounded-[var(--borderRadius)] bg-[var(--main-background)] px-[15px] py-[10px]">
  221. <div className="flex items-center justify-between border-b-[1px] border-[var(--primary-button)] pb-[.1rem]">
  222. <Image
  223. src={HeaderImageMap.get(1)?.img || "/img/avatar.webp"}
  224. className={"mr-[.1rem] h-[58px] w-[58px] rounded-[50%]"}
  225. alt={"avatar"}
  226. width={120}
  227. height={120}
  228. />
  229. <div className="flex flex-1 flex-col justify-between">
  230. <div className="font-black">
  231. {cryptoStr(data?.self_rank?.nickName)}
  232. </div>
  233. <div>Apostas: 0</div>
  234. </div>
  235. <CustomButton className="!px-[.1rem] !py-[.08rem]">
  236. Aposte
  237. </CustomButton>
  238. </div>
  239. <div className="mt-[10px] flex items-center">
  240. <div className="flex flex-1 flex-col items-center justify-between border-r-[1px] border-[var(--primary-button)]">
  241. <div className="text-[14px] font-black">Meu ranking</div>
  242. <div className="mt-[8px] text-[16px] font-black">
  243. Fora do ranking
  244. </div>
  245. </div>
  246. <div className="flex flex-1 flex-col items-center justify-between">
  247. <div className="text-[14px] font-black">Prêmio</div>
  248. <div className="mt-[8px] text-[14px] font-black">
  249. {data?.self_rank?.reward && data.self_rank.reward[0]
  250. ? data.self_rank.reward[0].amount
  251. : 0}{" "}
  252. <span className="text-[var(--textColor4)]">
  253. (
  254. {data?.self_rank?.reward && data.self_rank.reward[0]
  255. ? data.self_rank.reward[0].ratio
  256. : 0}
  257. %)
  258. </span>
  259. </div>
  260. </div>
  261. </div>
  262. </div>
  263. <h3 className="py-[.1rem] text-center text-[14px] text-[var(--textColor2)]">
  264. Aposte para subir no ranking
  265. </h3>
  266. <div className="mt-[.1rem] flex items-center justify-between">
  267. <CustomButton
  268. className={styles.agentBtn}
  269. onClick={() => router.push("/rank/history")}
  270. >
  271. <i className="iconfont icon-huodejiangli mr-[.1rem] scale-[1.4] transform text-[var(--primary-button)]"></i>
  272. <span className="text-[var(--textColor1)]">
  273. Prêmio histórico
  274. </span>
  275. </CustomButton>
  276. <CustomButton
  277. className={styles.agentBtn}
  278. onClick={() => router.push("/rank/rules")}
  279. >
  280. <i className="iconfont icon-wenjian mr-[.1rem] scale-[1.4] transform text-[var(--primary-button)]"></i>
  281. <span className="text-[var(--textColor1)]">Regras</span>
  282. </CustomButton>
  283. </div>
  284. <div>
  285. <div className="flex items-center px-[10px] py-[10px] text-[.13rem] font-black">
  286. <div className="w-[40px] text-center">Rank</div>
  287. <div className="w-[100px] text-center">Jogador</div>
  288. <div className="w-[80px] text-center">Apostas</div>
  289. <div className="flex-1 text-right">Prêmio</div>
  290. </div>
  291. {!!rankList?.length &&
  292. rankList.map((item) => {
  293. let curReward: RankReward = {} as RankReward;
  294. if (item?.reward?.length) {
  295. curReward = item?.reward[0];
  296. }
  297. return (
  298. <div
  299. key={item.rank}
  300. className="flex items-center px-[10px] py-[10px] text-[.12rem] font-normal"
  301. >
  302. <div className="w-[40px] text-center">
  303. {getNumber(item.rank)}
  304. </div>
  305. <div className="w-[100px] text-center">
  306. {cryptoStr(item?.nickName)}
  307. </div>
  308. <div className="w-[80px] text-center text-[var(--textColor4)]">
  309. {item.score}
  310. </div>
  311. <div className="flex-1 text-right text-[var(--textColor4)]">
  312. R${curReward?.amount || 0}
  313. <span className="text-[var(--textColor1)]">
  314. ({curReward?.ratio || 0}%)
  315. </span>
  316. </div>
  317. </div>
  318. );
  319. })}
  320. <InfiniteScroll
  321. hasMore={!noMore}
  322. loadMore={async () => {
  323. pageInfo.current.page++;
  324. await getData(areaId);
  325. }}
  326. ></InfiniteScroll>
  327. </div>
  328. </div>
  329. </div>
  330. </div>
  331. </main>
  332. </div>
  333. );
  334. };
  335. export default Page;